﻿using System.Text.Json;
using FytSoa.Application.Car;
using FytSoa.Domain.Car;
using FytSoa.Domain.Core.Param;
using FytSoa.Domain.Core.Result;
using FytSoa.Domain.Sys;
using FytSoa.Domain.User;
using FytSoa.Sugar;
using Microsoft.AspNetCore.Mvc;

namespace FytSoa.Application.App;
[ApiExplorerSettings(GroupName = "v5")]
public class AppPrizeService: IApplicationService
{
    private readonly CarsPrizelogService _prizelogService;
    private readonly SugarRepository<CarsPrize> _thisRepository;
    public AppPrizeService(CarsPrizelogService prizelogService
    ,SugarRepository<CarsPrize> thisRepository)
    {
        _prizelogService = prizelogService;
        _thisRepository = thisRepository;
    }

    /// <summary>
    /// 查询中奖日志
    /// </summary>
    /// <param name="param"></param>
    /// <returns></returns>
    public async Task<PageResult<CarsPrizelogDto>> GetPagesAsync(PrizePageParam param)=>
        await _prizelogService.GetPagesAsync(param);

    /// <summary>
    /// 根据用户查询抽奖次数
    /// </summary>
    /// <returns></returns>
    public async Task<int> GetUserDrawAsync(long id)
    {
        var userRepository = _thisRepository.ChangeRepository<SugarRepository<Member>>();
        var user = await userRepository.GetByIdAsync(id);
        return user.PrizeDrawNum;
    }
    
    private static readonly Random random = new();
    /// <summary>
    /// 返回一个根据用户抽奖获得奖品编号
    /// </summary>
    /// <returns></returns>
    public async Task<AppSelectedPrizeDto> GetPrizeTurnAsync(AppPrizeTurnParam param)
    {
        var userRepository = _thisRepository.ChangeRepository<SugarRepository<Member>>();
        var user = await userRepository.GetByIdAsync(param.UserId);
        //判断用户是否存在以及次数是否足够
        if (user==null || user.PrizeDrawNum==0)
        {
            throw new ArgumentException("用户抽奖次数不足~");
        }
        
        var prizeModel = await _thisRepository.GetByIdAsync(param.PrizeId);
        var prizes = prizeModel.Prizes.Select(m => m.Ratio);
        InitAdRewardWeight(prizes.ToArray());
        //查询奖品内定用户是否得几等奖
        var interanl = 0;
        var isInteranl = false;
        for (int i = 0; i < prizeModel.Prizes.Count; i++)
        {
            var item = prizeModel.Prizes[i];
            if (item.Users.All(m => m != param.UserId.ToString())) continue;
            interanl = i;
            isInteranl = true;
            break;
        }
        //根据活动比例进行随机抽奖
        var index = GetRandomIndex();
        if (isInteranl)
        {
            index = interanl;
        }
        var model=prizeModel.Prizes[index];
        var resNum = index;
        if (index==1)
        {
            resNum = 2;
        }
        if (index==2)
        {
            resNum = 4;
        }
        //判断是否中奖
        var res = new AppSelectedPrizeDto
        {
            Num = resNum
        };
        
        //更新用户抽奖次数
        user.PrizeDrawNum -= 1;
        await userRepository.UpdateAsync(user);
        
        if (model.Name == "谢谢参与") return res;
        res.Status = true;
        res.Summary = model.Name + "：" + model.Code;
        await _prizelogService.AddAsync(new CarsPrizelogDto()
        {
            UserId = param.UserId,
            PrizeId=param.PrizeId,
            PrizeInfo=model.Name+"："+model.Code
        });
        //增加中奖数量
        prizeModel.PickCount += 1;
        await _thisRepository.UpdateAsync(prizeModel);
        
        //Console.WriteLine("本次中奖为："+ JsonSerializer.Serialize(prizeModel.Prizes[index]));
        return res;
    }
    public static (float, int)[] PrepareAdRewardWeight;
    public static void InitAdRewardWeight(int[] weightList)
    {
        var total = weightList.Sum();
        int length = weightList.Length;
        var avg = 1f * total / length;
        List<(float, int)> smallAvg = new List<(float, int)>();
        List<(float, int)> bigAvg = new List<(float, int)>();
        for (int i = 0; i < weightList.Length; i++)
        {
            (weightList[i] > avg ? bigAvg : smallAvg).Add((weightList[i], i));
        }
        PrepareAdRewardWeight = new (float, int)[weightList.Length];
        for (int i = 0; i < weightList.Length; i++)
        {
            if (smallAvg.Count > 0)
            {
                if (bigAvg.Count > 0)
                {
                    PrepareAdRewardWeight[smallAvg[0].Item2] = (smallAvg[0].Item1 / avg, bigAvg[0].Item2);
                    bigAvg[0] = (bigAvg[0].Item1 - avg + smallAvg[0].Item1, bigAvg[0].Item2);
                    if (avg - bigAvg[0].Item1 > 0.0000001f)
                    {
                        smallAvg.Add(bigAvg[0]);
                        bigAvg.RemoveAt(0);
                    }
                }
                else
                {
                    PrepareAdRewardWeight[smallAvg[0].Item2] = (smallAvg[0].Item1 / avg, smallAvg[0].Item2);
                }
                smallAvg.RemoveAt(0);
            }
            else
            {
                PrepareAdRewardWeight[bigAvg[0].Item2] = (bigAvg[0].Item1 / avg, bigAvg[0].Item2);
                bigAvg.RemoveAt(0);
            }
        }
    }
    
    public static int GetRandomIndex()
    {
        var randomNum = random.NextDouble() * PrepareAdRewardWeight.Length;
        int intRan = (int)Math.Floor(randomNum);
        var p = PrepareAdRewardWeight[intRan];
        if (p.Item1 > randomNum - intRan)
        {
            return intRan;
        }
        else
        {
            return p.Item2;
        }
    }
}
